package com.kingyea.mobilepolice.apimanage.specification;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.kingyea.mobilepolice.apimanage.paraminfo.bean.ParamInfo;
import com.kingyea.mobilepolice.apimanage.paraminfo.service.ParamInfoService;
import com.kingyea.mobilepolice.apimanage.responseinfo.bean.ResponseInfo;
import com.kingyea.mobilepolice.apimanage.responseinfo.service.ResponseInfoService;
import com.kingyea.mobilepolice.apimanage.serviceinfo.bean.ServiceInfo;
import com.kingyea.mobilepolice.apimanage.serviceinfo.service.ServiceInfoService;
import com.kingyea.mobilepolice.apimanage.serviceinfo.vo.ServiceInfoVO;
import com.kingyea.mobilepolice.apimanage.specification.bean.*;
import com.kingyea.mobilepolice.apimanage.utils.page.PageInfo;
import com.kingyea.mobilepolice.config.feign.Oauth2ClientProperties;
import com.kingyea.mobilepolice.system.Result;
import com.kingyea.mobilepolice.system.aspect.NotResult;
import com.kingyea.mobilepolice.user.bean.User;
import io.swagger.annotations.Api;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author cxc
 * @version v1.0.0
 * @description
 * @date Created in 2018-05-10 9:58
 */
@Api(value = "移动警务应用服务总线", description = "集成规范接口")
@RequestMapping(value = "/specification")
@RestController
public class SpecificationController {

    private final static String USER_KEY = "user";
    @Autowired
    private Oauth2ClientProperties oauth2ClientProperties;

    @Autowired
    private ServiceInfoService serviceInfoService;

    @Autowired
    private ParamInfoService paramInfoService;

    @Autowired
    private ResponseInfoService responseInfoService;

    @Autowired
    @Qualifier("initRestTemplate")
    private RestTemplate restTemplate;

    /**
     * @Description: 服务目录调用接口，应用开发平台通过此接口获取可用服务接口的完整示例
     * @param: [condition]
     * @return: java.lang.Object
     * @author: cxc
     * @Date: 2018-05-10
     */
    @RequestMapping(value = "/publishedlist")
    @NotResult
    public Object publishedlist(SpecificationCondition condition, Model model) throws Exception {
        try {
            User user = (User) model.asMap().get(USER_KEY);
            System.out.println(JSON.toJSONString(user));
            condition.setAppid(Optional.ofNullable(condition.getAppid()).filter(StringUtils::isNotBlank).map(String::trim).orElse(null));
            Map<String, Object> map = JSON.parseObject(JSON.toJSONString(condition), Maps.newHashMap().getClass());
            PageInfo<ServiceInfoVO> page = serviceInfoService.searchByCondition(map);
            List<ServiceInfoVO> serviceInfoVoList = page.getList();
            return new SpecilficationBaseResponse(serviceInfoVoList.stream().map(b -> {
                SpecificationServiceInfo vo = new SpecificationServiceInfo();
                BeanUtils.copyProperties(b, vo);
                vo.setDevelopLanguage("java");//wu
                vo.setDevelopProtocol("restful");//wu
                vo.setPublished(b.getIsOnline() == 1 ? Boolean.TRUE : Boolean.FALSE);
                vo.setArticulatedStandard("挂接标准规范");//wu
                vo.setCategory(String.valueOf(b.getApiType()));//TODO 查询接口种类
                //auditime -> publishedTime
                vo.setShareScopeId("shareScopeId is not");//scope ?
                vo.setPoliceType("");
                return vo;
            }).collect(Collectors.toList()));
        } catch (Exception e) {

        }
        return new SpecilficationBaseResponse(ResponseCode.ERROR);
    }

    /**
     * @Description: 服务授权接口，应用开发平台通过此接口向总线平台服务提供授权服务给应用的信息，支持同时将多个服务授权给app应用
     * @param: [condition]
     * @return: java.lang.Object
     * @author: cxc
     * @Date: 2018-05-10
     */
    @RequestMapping(value = "/grant")
    @NotResult
    public Object grant(SpecificationCondition condition, Model model) {
        User user = (User) model.asMap().get(USER_KEY);
        System.out.println(JSON.toJSONString(user));
        Optional.ofNullable(condition.getAppid()).filter(StringUtils::isNotBlank).orElseThrow(() -> new RuntimeException(ResponseCode.QUERY_PARAM_IS_NULL.name()));
        Optional.ofNullable(condition.getAppkey()).filter(StringUtils::isNotBlank).orElseThrow(() -> new RuntimeException(ResponseCode.QUERY_PARAM_IS_NULL.name()));
        Optional.ofNullable(condition.getAppname()).filter(StringUtils::isNotBlank).orElseThrow(() -> new RuntimeException(ResponseCode.QUERY_PARAM_IS_NULL.name()));
        Preconditions.checkArgument(condition.getIds() != null && !condition.getIds().isEmpty(), ResponseCode.QUERY_PARAM_IS_NULL);
        System.out.println(JSON.toJSONString(condition));
        Map<String, Object> map = JSON.parseObject(JSON.toJSONString(condition), Maps.newHashMap().getClass());
        return new SpecificationGrantResponse();
    }

    /**
     * @Description: 服务使用说明调用接口，应用开发平台通过此接口获取服务接口示例
     * @param: [condition]
     * @return: java.lang.Object
     * @author: cxc
     * @Date: 2018-05-10
     */
    @RequestMapping(value = "/instructions")
    @NotResult
    public Object instructions(SpecificationCondition condition, Model model) throws Exception {
        User user = (User) model.asMap().get(USER_KEY);
        System.out.println(JSON.toJSONString(user));
        //id 必填
        Optional.ofNullable(condition.getId()).filter(StringUtils::isNotBlank).orElseThrow(() -> new RuntimeException(ResponseCode.QUERY_PARAM_IS_NULL.name()));
        ServiceInfo serviceInfo = serviceInfoService.getBySerialNo(condition.getId());
        //服务不存在
        Optional.ofNullable(serviceInfo).orElseThrow(() -> new RuntimeException(ResponseCode.QUERY_TARGET_IS_NULL.name()));
        //请求类型
        List<ParamInfo> paramInfoList = paramInfoService.getParamInfoByServiceId(serviceInfo.getId());
        List<SpecificationParamInfo> spiList = paramInfoList.stream().map(p -> {
            SpecificationParamInfo spi = new SpecificationParamInfo();
            BeanUtils.copyProperties(p, spi);
            spi.setTypeName(String.valueOf(p.getType()));
            //TODO 未知
            spi.setSupport("equal");
            return spi;
        }).collect(Collectors.toList());
        //返回类型
        List<ResponseInfo> responseInfoList = responseInfoService.getByServiceId(serviceInfo.getId());
        List<SpecificationResponseInfo> sriList = responseInfoList.stream().map(r -> {
            SpecificationResponseInfo sri = new SpecificationResponseInfo();
            BeanUtils.copyProperties(r, sri);
            return sri;
        }).collect(Collectors.toList());
        SpecificationInstructionsResponse sir = new SpecificationInstructionsResponse();
        //serviceInfo message
        sir.setInput(spiList);
        sir.setOutput(sriList);
        sir.setUrl(serviceInfo.getServiceAddress() + serviceInfo.getPublicPath());
        sir.setComment(serviceInfo.getDescription());
//        resp.setHeader("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
        return sir;
    }

    /**
     * @Description: 移动应用服务对已得到授权的服务进行调用，在调用时按照各服务接口要求传递参数和获取服务结果。
     * @param: [condition]
     * @return: java.lang.Object
     * @author: cxc
     * @Date: 2018-05-10
     */
    @RequestMapping(value = "/需调用接口名")
    @NotResult
    public Object 需调用接口名(SpecificationCondition condition, User user) {
        System.out.println(JSON.toJSONString(condition));
        Map<String, Object> map = JSON.parseObject(JSON.toJSONString(condition), Maps.newHashMap().getClass());
        return new SpecilficationBaseResponse();
    }

    @ModelAttribute
    public void modelAttribute(@RequestParam(name = "access_token", required = false) String accessToken, Model model, SpecificationCondition condition) {
        System.out.println("-------------------------------------");
        accessToken = Optional.ofNullable(accessToken).filter(StringUtils::isNotBlank).orElseGet(() -> {
            Optional.ofNullable(condition.getUsername()).filter(StringUtils::isNotBlank).map(String::trim).orElseThrow(() -> new RuntimeException(ResponseCode.QUERY_PARAM_IS_NULL.name()));
            Optional.ofNullable(condition.getPassword()).filter(StringUtils::isNotBlank).map(String::trim).orElseThrow(() -> new RuntimeException(ResponseCode.QUERY_PARAM_IS_NULL.name()));
            return getToken(condition.getUsername(), condition.getPassword(), "*");
        });
        User user = getUserByToken(accessToken);
        System.out.println(JSON.toJSONString(user));
        System.out.println("--------------------=-----------------");
        model.addAttribute(USER_KEY, user);
    }

    private User getUserByToken(String accessToken) {
        String url = "http://auth-server" + "/user/getCurrentUserInfo" + "?" + "access_token=" + accessToken;
        Result<User> userResult = restTemplate.getForObject(url, new Result<User>().getClass());
        return JSON.parseObject(JSON.toJSONString(userResult.getData()), User.class);
    }

    private String getToken(String username, String password, String scope) {
        try {
            String base = "Basic " + new String(Base64.encodeBase64((oauth2ClientProperties.getClientId() + ":" + oauth2ClientProperties.getClientSecret()).getBytes(Charset.forName("US-ASCII"))));
            LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
            map.add("scope", scope);
            map.add("username", username);
            map.add("password", password);
            map.add("grant_type", "password");
            HttpHeaders headers = new HttpHeaders();
            headers.set("Authorization", base);
            HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(map, headers);
            RestTemplate restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new FastJsonHttpMessageConverter());
            String str = restTemplate.postForObject(oauth2ClientProperties.getAccessTokenUrl(), httpEntity, String.class);
            return JSON.parseObject(str, AccessTokenBean.class).getToken();
        } catch (HttpClientErrorException e) {
            throw new RuntimeException(ResponseCode.QUERY_TARGET_IS_NULL.name());
        } catch (Exception e) {
            throw new RuntimeException(ResponseCode.ERROR.name());
        }
    }

    @ExceptionHandler(Exception.class)
    public SpecilficationBaseResponse exceptionHandler(Exception e) {
        System.out.println("【error】");
        e.printStackTrace();
        return new SpecilficationBaseResponse(Optional.ofNullable(e.getMessage()).filter(msg -> ResponseCode.contains(msg)).map(msg -> ResponseCode.valueOf(msg)).orElse(ResponseCode.ERROR));
    }

    /*    服务接口调用返回状态码
        代码	说明
    10	查询请求成功
    11	查询请求失败，参数错误或缺失
    12	查询请求失败，参数不是指定结构，无法解析
    19	查询请求失败，服务出错
        补充HTTP状态码 使用api 调用时有效（常见）
        代码	说明
    404	服务地址不存在，地址不正确或服务已关闭
    401	用于认证的用户名密码不正确
    500	（服务器内部错误） 服务器遇到错误，无法完成请求。
    503	服务不可用*/
}

class AccessTokenBean {
    @JSONField(name = "access_token")
    private String token;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}